/*
 * Copyright 2002-2006 the original author or authors. Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions and limitations under the
 * License.
 */
package pikes.samples.countries.ui.peak;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import org.springframework.beans.support.RefreshablePagedListHolder;
import org.springframework.samples.countries.web.bom.Window;
import org.springframework.samples.countries.web.translate.ClientSide;
import org.springframework.web.servlet.support.RequestContext;

import pikes.ecma.Assignment;
import pikes.ecma.ExpressionStatement;
import pikes.ecma.FunctionCall;
import pikes.ecma.FunctionExpression;
import pikes.ecma.IndexedAccess;
import pikes.ecma.MemberPropertyAccess;
import pikes.ecma.StringLiteral;
import pikes.html.xhtml.Block;
import pikes.html.xhtml.Division;
import pikes.html.xhtml.Flow;
import pikes.html.xhtml.Inline;
import pikes.html.xhtml.PCData;
import pikes.html.xhtml.table.TR;
import pikes.html.xhtml.table.Table;
import pikes.html.xhtml.table.TableCell;
import pikes.peak.ApplicationContextMessageProvider;
import pikes.peak.BlockComponent;
import pikes.peak.CellDecorator;
import pikes.peak.ComboBox;
import pikes.peak.Component;
import pikes.peak.Composite;
import pikes.peak.FormPanel;
import pikes.peak.Grid;
import pikes.peak.GridDecorator;
import pikes.peak.InlineComponent;
import pikes.peak.InputText;
import pikes.peak.InternalURI;
import pikes.peak.Label;
import pikes.peak.LabelStyle;
import pikes.peak.ObjectFormatter;
import pikes.peak.RequestProperty;
import pikes.peak.Row;
import pikes.peak.RowDecorator;
import pikes.peak.SelfURI;
import pikes.peak.Text;
import pikes.peak.Paragraph;
import pikes.peak.TextGroupStyle;
import pikes.peak.TextParameters;
import pikes.peak.URIParameterBuilder;
import pikes.peak.VisibilityCondition;
import pikes.samples.countries.Country;
import pikes.samples.countries.ui.CountriesFilter;
import pikes.uri.URI;

public class MainPage extends BehaviourPage {

	static List<Integer> SELECTABLE_PAGE_SIZES = new ArrayList<Integer>();

	static String SELECTABLE_CODE_VALUES_STRING = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,W,X,Y,Z";

	static List<String> SELECTABLE_CODE_VALUES = new ArrayList<String>();

	static {
		SELECTABLE_PAGE_SIZES.add(5);
		SELECTABLE_PAGE_SIZES.add(10);
		SELECTABLE_PAGE_SIZES.add(15);
		SELECTABLE_PAGE_SIZES.add(20);
		SELECTABLE_PAGE_SIZES.add(30);
		SELECTABLE_PAGE_SIZES.add(40);
		SELECTABLE_PAGE_SIZES.add(80);

		SELECTABLE_CODE_VALUES.add("");
		StringTokenizer tokenizer = new StringTokenizer(SELECTABLE_CODE_VALUES_STRING, ",");
		while (tokenizer.hasMoreTokens()) {
			SELECTABLE_CODE_VALUES.add(tokenizer.nextToken());
		}
	}

	private ComboBox<Integer> pageSizeCombo = new ComboBox<Integer>("pageSize", new RequestProperty<Collection<Integer>>() {

		public Collection<Integer> getValue(Map model, RequestContext rc) throws Exception {
			return SELECTABLE_PAGE_SIZES;
		}

	});
	
	private InputText nameFilterInputText = new InputText("filter.name", new FilterName());
	
	private ComboBox<String> codeFilterCombobox = new ComboBox<String>("filter.code",
			new RequestProperty<Collection<String>>() {

				public Collection<String> getValue(Map model, RequestContext rc) throws Exception {
					return SELECTABLE_CODE_VALUES;
				}

			});
	
	private VisibilityCondition ifCountriesPresent = new VisibilityCondition() {

		public boolean isTrue(Map model, RequestContext requestContext) throws Exception {
			return model.containsKey("countries");
		}

	};
	
	final InternalURI detailPageURI = new InternalURI("/main/detail.htm", new URIParameterBuilder() {

		@Override
		public void build(Map<CharSequence, CharSequence> parameters) {
			parameters.put("code", "");
		}

	});
	
	protected Window window = new Window();

	@Override
	protected void translate(Collection<DynamicSourceElements> declaredCode) {
		DynamicSourceElements dynamincSourceElements1 = new DynamicSourceElements();

		DynamicFunctionDeclaration dynamicFunction = new DynamicFunctionDeclaration("go");
		dynamicFunction.addFormalParameter("country");

		DynamicVariableStatement dynvarDec = new DynamicVariableStatement("url", new DynamicAddition(detailPageURI,
				"country"));
		dynamicFunction.addCode(dynvarDec);
		dynamicFunction.addCode(new Assignment(new MemberPropertyAccess("window", "location"), "url"));

		dynamincSourceElements1.addSourceElement(dynamicFunction);
		declaredCode.add(dynamincSourceElements1);

		DynamicSourceElements dynamincSourceElements2 = new DynamicSourceElements();

		FunctionExpression windowOnloadFunction = new FunctionExpression();
		windowOnloadFunction.add(new FunctionCall("tableruler"));
		windowOnloadFunction.add(new FunctionCall("tablestripe"));

		dynamincSourceElements2.addSourceElement(new Assignment(new MemberPropertyAccess("window", "onload"),
				windowOnloadFunction));
		declaredCode.add(dynamincSourceElements2);
	}

	@Override
	protected String getTitle() {
		return "countries.main.htitle";
	}

	@ClientSide
	protected String returnNewStringWithParameter() {
		return new String("boo");
		// return new String(new byte[0]);
	}

	private CharSequence uri = null;

	protected void go(String country) {
		// String url = "/countries/main/detail.htm?code=" + country;
		String url = uri + country;
		window.location = url;
	}

	@Override
	protected void setupFixedReferenceURIs(Collection<RequestProperty<URI>> referencesURIs) {
		referencesURIs.add(new InternalURI("/js/countries.js"));
	}

	

	@Override
	protected void addLinkKeys(Set<String> linkKeys) {
		linkKeys.add("home");
		linkKeys.add("countries.main");
		linkKeys.add("countries.pdf");
		linkKeys.add("countries.excel");
	}

	@Override
	protected void buildContent(Composite content) {

		DynamicSourceElements submitForm = new DynamicSourceElements();

		FormPanel formPanel = new FormPanel();

		Grid countriesGrid = new Grid(new GridDecorator() {

			public void decorateTable(Table table, Map arg1, RequestContext arg2) throws Exception {
				table.setClazz("stripe ruler");
			}

		});

		RowDecorator headerRowDecorator = new RowDecorator() {

			public void decorateRow(TR tr, Map model, RequestContext requestContext) throws Exception {
				tr.setClazz("title");
			}

		};

		Grid pagerGrid = new Grid();

		submitForm.addSourceElement(new FunctionCall(new MemberPropertyAccess(new IndexedAccess(
				new MemberPropertyAccess("document", "forms"), "0"), "submit")));

		pageSizeCombo.setFormatter(new PageSizeFormatter());
		pageSizeCombo.setSelectedObjectProvider(new RequestProperty<Integer>() {

			public Integer getValue(Map model, RequestContext requestContext) throws Exception {
				int selectedRowNumberPerPage = -1;
				if (model.containsKey("countries")) {
					RefreshablePagedListHolder listHolder = (RefreshablePagedListHolder) model.get("countries");
					selectedRowNumberPerPage = listHolder.getPageSize();
				}
				return selectedRowNumberPerPage;
			}

		});
		pageSizeCombo.setOnChange(submitForm);

		nameFilterInputText.setOnChange(submitForm);

		codeFilterCombobox.setSelectedObjectProvider(new FilterCode());
		codeFilterCombobox.setFormatter(new ObjectFormatter<String>() {

			public Inline getLabel(String object, Map model, RequestContext requestContext) throws Exception {
				if ("".equals(object)) {
					return Label.SPACE.createHtml(model, requestContext);
				}
				else {
					return new PCData(object);
				}
			}

			public CharSequence getValue(String object, Map model, RequestContext requestContext) throws Exception {
				return object;
			}

		});
		codeFilterCombobox.setOnChange(submitForm);

		Label nameLabel = new Label("name");
		nameLabel.setURI(new SelfURI(new SortPropertyParameter("name")));
		Label codeLabel = new Label("code");
		codeLabel.setURI(new SelfURI(new SortPropertyParameter("code")));

		Row headerRow1 = new Row(headerRowDecorator);
		headerRow1.addHeaderCell(nameLabel);
		headerRow1.addHeaderCell(codeLabel);

		Row headerRow2 = new Row(headerRowDecorator);
		headerRow2.addCell(new Component[] { new Text("filter"), nameFilterInputText });
		headerRow2.addCell(new Component[] { new Text("filter"), codeFilterCombobox });

		List<RequestProperty<TR>> headerRows = new LinkedList<RequestProperty<TR>>();
		headerRows.add(headerRow1);
		headerRows.add(headerRow2);
		countriesGrid.setHeaderRows(headerRows);

		countriesGrid.setRows(new CountryRowCollectionProvider());

		formPanel.add(countriesGrid, ifCountriesPresent);

		formPanel.add(Composite.LINE);

		formPanel.add(new PagerNavigationComponent());

		Composite pageSizeComposite = new Composite();
		pageSizeComposite.setClazz("psize");
		pageSizeComposite.add(pageSizeCombo);
		formPanel.add(pageSizeComposite);

		Composite funnyText = new Composite();
		funnyText.add(new Text("-=+=-"));
		formPanel.add(funnyText);

		Composite pagerComposite = new Composite();
		pagerComposite.setClazz("pager");

		Row row = new Row();
		row.addCell(new Text("pg.pages", new TextParameters() {

			@Override
			protected void addParameters(List<Object> parameters, Map model, RequestContext requestContext)
					throws Exception {
				if (model.containsKey("countries")) {
					RefreshablePagedListHolder listHolder = (RefreshablePagedListHolder) model.get("countries");
					parameters.add(listHolder.getPage() + 1);
					parameters.add(listHolder.getPageCount());
				}
			}

		}), new CellDecorator() {

			public void decorateCell(TableCell cell, Map model, RequestContext requestContext) throws Exception {
				cell.setAlign(TableCell.Align.LEFT);
			}

		});

		row.addCell(new Text(new RequestProperty<CharSequence>() {

			public CharSequence getValue(Map model, RequestContext requestContext) throws Exception {
				if (model.containsKey("countries")) {
					RefreshablePagedListHolder listHolder = (RefreshablePagedListHolder) model.get("countries");
					DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM,
							requestContext.getLocale());
					return format.format(listHolder.getRefreshDate());
				}
				else {
					return null;
				}

			}

		}), new CellDecorator() {

			public void decorateCell(TableCell cell, Map arg1, RequestContext arg2) throws Exception {
				cell.setAlign(TableCell.Align.CENTER);
				cell.setId("currentDate");
			}

		});
		row.addCell(new Text("pg.records", new TextParameters() {

			@Override
			protected void addParameters(List<Object> parameters, Map model, RequestContext requestContext)
					throws Exception {
				if (model.containsKey("countries")) {
					RefreshablePagedListHolder listHolder = (RefreshablePagedListHolder) model.get("countries");
					parameters.add(listHolder.getFirstElementOnPage() + 1);
					parameters.add(listHolder.getLastElementOnPage() + 1);
					parameters.add(listHolder.getNrOfElements());
				}

			}

		}), new CellDecorator() {

			public void decorateCell(TableCell cell, Map arg1, RequestContext arg2) throws Exception {
				cell.setAlign(TableCell.Align.RIGHT);
			}

		});
		pagerGrid.setRow(row);
		pagerComposite.add(pagerGrid, ifCountriesPresent);
		formPanel.add(pagerComposite);

		formPanel.add(Composite.LINE);

		Paragraph sortParagraph = new Paragraph("sort.crt");
		sortParagraph.add(new Text(new SortProperty()));
		sortParagraph.add(Label.SPACE);
		sortParagraph.add(new Text(new SortDirection()));
		formPanel.add(sortParagraph);

		content.add(new Paragraph("countries.main.title", TextGroupStyle.LEVEL_2));

		content.add(formPanel);
	}
}

class CountryRowCollectionProvider implements RequestProperty<Collection<RequestProperty<TR>>> {

	public Collection<RequestProperty<TR>> getValue(Map model, RequestContext requestContext) throws Exception {

		RefreshablePagedListHolder listHolder = (RefreshablePagedListHolder) model.get("countries");

		List<Country> countries = listHolder.getPageList();

		List<RequestProperty<TR>> rows = new LinkedList<RequestProperty<TR>>();

		Iterator<Country> iCountries = countries.iterator();

		while (iCountries.hasNext()) {
			final Country country = iCountries.next();
			Row row = new Row(new CountryRowDecorator(country));
			row.addCell(new Component() {

				public Flow createHtml(Map arg0, RequestContext arg1) throws Exception {
					return new PCData(country.getName());
				}

			});
			row.addCell(new Component() {

				public Flow createHtml(Map arg0, RequestContext arg1) throws Exception {
					return new PCData(country.getCode());
				}

			});
			rows.add(row);
		}

		return rows;
	}

}

class CountryRowDecorator implements RowDecorator {

	private FunctionCall goFunction = null;

	public CountryRowDecorator(Country country) {
		this.goFunction = new FunctionCall("go", new StringLiteral(country.getCode()));
	}

	public void decorateRow(TR tr, Map arg1, RequestContext arg2) throws Exception {
		tr.setOnClick(new ExpressionStatement(goFunction));
	}

}

class FilterName implements RequestProperty<CharSequence> {

	public CharSequence getValue(Map model, RequestContext requestContext) throws Exception {
		String filterNameValue = "";
		if (model.containsKey("countries")) {
			RefreshablePagedListHolder listHolder = (RefreshablePagedListHolder) model.get("countries");
			CountriesFilter filter = (CountriesFilter) listHolder.getFilter();
			filterNameValue = filter.getName();
		}
		return filterNameValue;
	}

}

class FilterCode implements RequestProperty<String> {

	public String getValue(Map model, RequestContext requestContext) throws Exception {
		String filterCodeValue = "";
		if (model.containsKey("countries")) {
			RefreshablePagedListHolder listHolder = (RefreshablePagedListHolder) model.get("countries");
			CountriesFilter filter = (CountriesFilter) listHolder.getFilter();
			filterCodeValue = filter.getCode();
		}
		return filterCodeValue;
	}

}

class SortDirection implements RequestProperty<CharSequence> {

	private RequestProperty<CharSequence> ascendingText = new ApplicationContextMessageProvider("ascending");

	private RequestProperty<CharSequence> descendingText = new ApplicationContextMessageProvider("descending");

	public CharSequence getValue(Map model, RequestContext requestContext) throws Exception {
		if (model.containsKey("countries")) {
			RefreshablePagedListHolder listHolder = (RefreshablePagedListHolder) model.get("countries");
			if (listHolder.getSort().isAscending()) {
				return ascendingText.getValue(model, requestContext);
			}
			else {
				return descendingText.getValue(model, requestContext);
			}
		}

		return null;
	}

}

class SortProperty implements RequestProperty<CharSequence> {

	public CharSequence getValue(Map model, RequestContext requestContext) throws Exception {
		if (model.containsKey("countries")) {
			RefreshablePagedListHolder listHolder = (RefreshablePagedListHolder) model.get("countries");

			ApplicationContextMessageProvider cmp = new ApplicationContextMessageProvider(listHolder.getSort()
					.getProperty());

			return cmp.getValue(model, requestContext);
		}

		return null;
	}

}

class PageSizeFormatter implements ObjectFormatter<Integer> {

	private RequestProperty<CharSequence> psize = new ApplicationContextMessageProvider("psize");

	public Inline getLabel(Integer object, Map model, RequestContext requestContext) throws Exception {
		PCData pcData = new PCData(object.toString());
		pcData.addText(" ");
		pcData.addText(psize.getValue(model, requestContext));
		return pcData;
	}

	public CharSequence getValue(Integer object, Map model, RequestContext requestContext) throws Exception {
		return object.toString();
	}

}

class PagerNavigationComponent implements BlockComponent {

	private Text dots = new Text(new RequestProperty<CharSequence>() {

		public CharSequence getValue(Map arg0, RequestContext arg1) throws Exception {
			return "...";
		}

	});

	public Block createHtml(Map model, RequestContext requestContext) throws Exception {
		Division pagerNav = new Division();
		pagerNav.setClazz("pagernav");

		if (model.containsKey("countries")) {
			RefreshablePagedListHolder listHolder = (RefreshablePagedListHolder) model.get("countries");

			if (listHolder.getPageCount() > 1) {
				pagerNav.add(new PageLink(0).createHtml(model, requestContext));

				pagerNav.add(Label.SPACE.createHtml(model, requestContext));
				pagerNav.add(Label.SPACE.createHtml(model, requestContext));
				pagerNav.add(dots.createHtml(model, requestContext));
				pagerNav.add(Label.SPACE.createHtml(model, requestContext));
				pagerNav.add(Label.SPACE.createHtml(model, requestContext));

				for (int pageIndex = listHolder.getFirstLinkedPage(); pageIndex <= listHolder.getLastLinkedPage(); pageIndex++) {
					if (pageIndex == listHolder.getPage()) {
						pagerNav.add(new Label(Integer.toString(pageIndex + 1), LabelStyle.STRONG).createHtml(model,
								requestContext));
					}
					else {
						pagerNav.add(Label.SPACE.createHtml(model, requestContext));
						pagerNav.add(new PageLink(pageIndex).createHtml(model, requestContext));
					}
				}

				pagerNav.add(Label.SPACE.createHtml(model, requestContext));
				pagerNav.add(Label.SPACE.createHtml(model, requestContext));
				pagerNav.add(dots.createHtml(model, requestContext));
				pagerNav.add(Label.SPACE.createHtml(model, requestContext));
				pagerNav.add(Label.SPACE.createHtml(model, requestContext));

				pagerNav.add(new PageLink(listHolder.getPageCount() - 1).createHtml(model, requestContext));
			}
		}

		return pagerNav;
	}

}

class PageLink implements InlineComponent {

	private int pageIndex = -1;

	public PageLink(int pageIndex) {
		this.pageIndex = pageIndex;
	}

	public Inline createHtml(Map model, RequestContext requestContext) throws Exception {
		Label label = new Label(Integer.toString(pageIndex + 1));
		label.setURI(new SelfURI(new PageParameter(pageIndex)));

		return label.createHtml(model, requestContext);
	}

}

class SortPropertyParameter extends URIParameterBuilder {

	private String property = null;

	public SortPropertyParameter(String property) {
		this.property = property;
	}

	@Override
	protected void build(Map<CharSequence, CharSequence> parameters) {
		parameters.put("sort.property", property);
	}
}

class PageParameter extends URIParameterBuilder {

	private int pageIndex = -1;

	public PageParameter(int pageIndex) {
		this.pageIndex = pageIndex;
	}

	@Override
	protected void build(Map<CharSequence, CharSequence> parameters) {
		parameters.put("page", Integer.toString(pageIndex));
	}
}
